from fadebuffer import FadeBuffer
import math
from numpy import *

class Node(object):
    def __init__(self, name, id):
        self.name = name
        self.id = id
        self.size = 1
        self.children = []
        self.parent = None

    def incSize(self):
        self.size += 1
        if self.parent: self.parent.incSize()

    def appendChild(self, name, id):
        self.children.append( Node(name, id) )
        self.children[-1].parent = self
        self.incSize()
        return self.children[-1]

    def removeChild(self, node):
        i = self.children.index( node )
        del self.children[i]
        node.parent = None

    def getPath(self):
        if self.parent is None: return [ self.name ]
        else:
            return self.parent.getPath() + [ self.name ]

def getRadius(node, angle, depth):
    try: radius = node.radius
    except: radius = None
    if radius is None or node.angleUsed != angle:
        depthradius = log(depth+1) * 0.5 + (random.random()*0.5/(depth+1) - 0.25/(depth+1))*0.5  #(random.random()*(0.1/(depth+1) - 0.1) - (0.1/(depth+1) - 0.1)*0.5)
        if node.parent.radius == 0.0:
            radius = depthradius
        else:
            radius = node.parent.radius * node.parent.angleUsed*1.5 + node.parent.radius
            radius += (radius-node.parent.radius)*random.random()*0.2 - (radius-node.parent.radius)*0.1
            radius = min( radius, depthradius )
        node.radius = radius
        node.angleUsed = angle
    return radius

def distribute(positions, sizes, node, angle1, angle2, depth = 0):
    angle = (angle1 + angle2)*0.5
    radius = getRadius(node, float(angle2 - angle1), depth)
    positions[node.id] = (cos(angle)*radius, sin(angle)*radius, 0)
    if radius > 0:
        sizes[node.id] = radius * float(angle2 - angle1) * 0.1
    else: sizes[node.id] = 0.1
    if node.children:
        arc = float(angle2 - angle1) / len(node.children)
        for i,child in enumerate(node.children):
            distribute(positions, sizes, child, angle1 + i*arc, angle1 + (i+1)*arc , depth + 1)

class LiveTree(object):
    def __init__(self, nodenumber = 10):
        self.fadebuffer = FadeBuffer( nodenumber )#, [(3, float32), (3, float32), (2, int), (float32,)] )
        self.pointBuffer = zeros( (nodenumber, 3), float32 )
        self.moveBuffer = zeros( (nodenumber, 3), float32 )
        self.edgeBuffer = zeros( (nodenumber, 2), int )
        self.sizeBuffer = zeros( (nodenumber,), float32 )
        self.alpha = self.fadebuffer.alpha
        id = self.fadebuffer.append()# [ (0,1,0), (0,0,0), (0,0) ] )
        self.setNodeData( id, (0,1,0), (0,0,0), (0,0), 0.1 )
        self.root = Node('root', id)
        self.root.radius = 0.0
        self.root.angleUsed = math.pi*2
        self.idtonode = { id : self.root }
        self.maxelements = nodenumber

    def setNodeData(self, id, pos, move, edge, size):
        self.pointBuffer[id] = pos
        self.moveBuffer[id] = move
        self.edgeBuffer[id] = edge
        self.sizeBuffer[id] = size

    def _addPath(self, node, path):
        self.fadebuffer.moveToFront( node.id )
        if path:
            selectedchild = None
            for child in node.children:
                if child.name == path[0]:
                    selectedchild = child
                    break
            if not selectedchild: selectedchild = self.addNode( node, path[0] )
            self._addPath( selectedchild, path[1:] )
            self.fadebuffer.moveToFront( node.id )

    def addPath(self, path):
        path = path.split('/')
        if not path[0]: path = path[1:]
        self._addPath(self.root, path)
        distribute(self.moveBuffer, self.sizeBuffer, self.root, 0, math.pi*2)

    def addNode(self, parent, name):
        if self.fadebuffer.full():
            id = self.fadebuffer.forceOneOut()
            self.removeNode( id )
        startpos = self.pointBuffer[parent.id]
        id = self.fadebuffer.nextPosition()
        assert id == self.fadebuffer.append()# [ startpos, (0,0,0), (id, parent.id) ] )
        self.setNodeData( id, startpos, (0,0,0), (id, parent.id), 0.1 )
        node = parent.appendChild(name, id)
        self.idtonode[id] = node
        return node

    def removeNode(self, id):
        node = self.idtonode[id]
        assert len(node.children) == 0
        node.parent.removeChild( node )

    def getMask(self): return self.fadebuffer.getMask()

    def update(self, elapsed):
        mask = self.fadebuffer.getMask()
        self.pointBuffer[mask] += (self.moveBuffer[mask] - self.pointBuffer[mask])*elapsed*0.5
        self.fadebuffer.fadeOff( elapsed * 0.05 )

    def removeDead(self): 
        removed = self.fadebuffer.removeDead()
        for id in removed: self.removeNode(id)
        distribute(self.moveBuffer, self.sizeBuffer, self.root, 0, math.pi*2)

#tree = LiveTree(10)
#tree.addPath('/cosa/nada')
#tree.fadebuffer.fadeOff(0.4)
#tree.addPath('/cosa/nada/jeje')
#tree.fadebuffer.fadeOff(0.4)
#tree.addPath('/cosa/ello')
#tree.fadebuffer.fadeOff(0.4)
#print tree.fadebuffer.alpha[ tree.fadebuffer.getMask() ]
#tree.addPath('/nada')
#tree.fadebuffer.fadeOff(0.4)
#tree.addPath('/nada/ello/jeje/mierda')
#tree.addPath('/nada/ello/cosa')
#tree.fadebuffer.fadeOff(0.4)
#tree.addPath('/ultimo')
#print tree.fadebuffer.alpha[ tree.fadebuffer.getMask() ]
